package top.codef.secure.config;

import java.nio.charset.StandardCharsets;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.security.jackson2.SecurityJackson2Modules;

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import top.codef.secure.config.conditions.RedisTemplateCreateCondition;
import top.codef.secure.config.interfaces.CerberusRedisJacksonMapperConfigure;

@Configuration
@Conditional(RedisTemplateCreateCondition.class)
@ConditionalOnClass({ RedisTemplate.class })
public class CerberusSecurityRedisConfig {

	@Autowired(required = false)
	public List<CerberusRedisJacksonMapperConfigure> cerberusRedisJacksonMapperConfigures;

	@Bean
	@ConditionalOnMissingBean(name = "securityRedisTemplate")
	public RedisTemplate<String, Object> securityRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
		StringRedisSerializer keyRedisSerializer = new StringRedisSerializer(StandardCharsets.UTF_8);
		redisTemplate.setKeySerializer(keyRedisSerializer);
		redisTemplate.setHashKeySerializer(keyRedisSerializer);
		redisTemplate.setConnectionFactory(redisConnectionFactory);
		RedisSerializer<Object> redisSerializer = redisSerializer();
		redisTemplate.setValueSerializer(redisSerializer);
		redisTemplate.setHashValueSerializer(redisSerializer);
		return redisTemplate;
	}

	@Bean(name = { "springSessionDefaultRedisSerializer", "redisSerializer" })
	public RedisSerializer<Object> redisSerializer() {
		ObjectMapper objectMapper = new ObjectMapper();
		objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(),
				ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
		objectMapper
				.registerModules(SecurityJackson2Modules.getModules(SecurityJackson2Modules.class.getClassLoader()));
		objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
		objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
		objectMapper.registerModule(new JavaTimeModule());
		if (cerberusRedisJacksonMapperConfigures != null)
			for (CerberusRedisJacksonMapperConfigure cerberusRedisJacksonMapperConfigure : cerberusRedisJacksonMapperConfigures)
				cerberusRedisJacksonMapperConfigure.configure(objectMapper);
		GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(
				objectMapper);
		return genericJackson2JsonRedisSerializer;
	}
}
